import smtplib
import requests
import logging
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from datetime import datetime, timedelta
from django.utils import timezone
from django.template import Template, Context
from django.core.mail import send_mail
from django.conf import settings
from django.contrib.auth import get_user_model
from django.db import transaction
from typing import Dict, List, Any, Optional

from .models import (
    NotificationTemplate, Notification, NotificationPreference,
    NotificationChannel, NotificationLog
)

User = get_user_model()
logger = logging.getLogger(__name__)


class TemplateService:
    """
    模板服务
    """
    
    def render_template(self, template: NotificationTemplate, context: Dict[str, Any]) -> Dict[str, str]:
        """
        渲染模板
        """
        try:
            # 渲染主题
            subject_template = Template(template.subject_template)
            subject = subject_template.render(Context(context))
            
            # 渲染内容
            content_template = Template(template.content_template)
            content = content_template.render(Context(context))
            
            return {
                'subject': subject,
                'content': content
            }
        except Exception as e:
            logger.error(f"模板渲染失败: {e}")
            raise
    
    def preview_template(self, template: NotificationTemplate, context: Dict[str, Any]) -> Dict[str, Any]:
        """
        预览模板
        """
        try:
            rendered = self.render_template(template, context)
            
            # 分析使用的变量
            variables_used = self._extract_variables(template.subject_template + template.content_template)
            missing_variables = [var for var in template.variables if var not in context]
            
            return {
                'subject': rendered['subject'],
                'content': rendered['content'],
                'variables_used': variables_used,
                'missing_variables': missing_variables
            }
        except Exception as e:
            logger.error(f"模板预览失败: {e}")
            raise
    
    def _extract_variables(self, template_content: str) -> List[str]:
        """
        提取模板中使用的变量
        """
        import re
        pattern = r'{{\s*(\w+)\s*}}'
        variables = re.findall(pattern, template_content)
        return list(set(variables))


class NotificationService:
    """
    通知服务
    """
    
    def __init__(self):
        self.template_service = TemplateService()
    
    def send_notification(
        self,
        template_id: int,
        recipient_id: int,
        context: Dict[str, Any],
        channels: List[str] = None,
        sender_id: int = None,
        priority: str = 'normal',
        scheduled_at: datetime = None
    ) -> Notification:
        """
        发送通知
        """
        try:
            # 获取模板和收件人
            template = NotificationTemplate.objects.get(id=template_id, is_active=True)
            recipient = User.objects.get(id=recipient_id)
            sender = User.objects.get(id=sender_id) if sender_id else None
            
            # 渲染模板
            rendered = self.template_service.render_template(template, context)
            
            # 创建通知记录
            notification = Notification.objects.create(
                recipient=recipient,
                sender=sender,
                template=template,
                notification_type=template.template_type,
                title=rendered['subject'],
                content=rendered['content'],
                data=context,
                channels=channels or ['system'],
                priority=priority,
                scheduled_at=scheduled_at
            )
            
            # 如果不是计划发送，立即发送
            if not scheduled_at or scheduled_at <= timezone.now():
                self._send_to_channels(notification)
            
            return notification
            
        except Exception as e:
            logger.error(f"发送通知失败: {e}")
            raise
    
    def batch_send_notifications(
        self,
        template_id: int,
        recipients: List[Dict[str, Any]],
        context: Dict[str, Any],
        channels: List[str] = None
    ) -> Dict[str, Any]:
        """
        批量发送通知
        """
        success_count = 0
        failed_count = 0
        errors = []
        
        try:
            with transaction.atomic():
                for recipient_data in recipients:
                    try:
                        # 合并上下文
                        recipient_context = {**context, **recipient_data.get('context', {})}
                        
                        self.send_notification(
                            template_id=template_id,
                            recipient_id=recipient_data['user_id'],
                            context=recipient_context,
                            channels=channels
                        )
                        success_count += 1
                        
                    except Exception as e:
                        failed_count += 1
                        errors.append({
                            'user_id': recipient_data['user_id'],
                            'error': str(e)
                        })
                        logger.error(f"批量发送失败 - 用户 {recipient_data['user_id']}: {e}")
            
            return {
                'success_count': success_count,
                'failed_count': failed_count,
                'errors': errors
            }
            
        except Exception as e:
            logger.error(f"批量发送通知失败: {e}")
            raise
    
    def _send_to_channels(self, notification: Notification):
        """
        发送到各个渠道
        """
        for channel in notification.channels:
            try:
                # 检查用户偏好
                if not self._check_user_preference(notification.recipient, notification.notification_type, channel):
                    continue
                
                # 创建日志记录
                log = NotificationLog.objects.create(
                    notification=notification,
                    channel=channel,
                    status='pending'
                )
                
                # 发送到具体渠道
                if channel == 'system':
                    self._send_system_notification(notification, log)
                elif channel == 'email':
                    self._send_email_notification(notification, log)
                elif channel == 'sms':
                    self._send_sms_notification(notification, log)
                elif channel == 'push':
                    self._send_push_notification(notification, log)
                elif channel == 'webhook':
                    self._send_webhook_notification(notification, log)
                
            except Exception as e:
                logger.error(f"发送到渠道 {channel} 失败: {e}")
                if 'log' in locals():
                    log.status = 'failed'
                    log.error_message = str(e)
                    log.save()
    
    def _check_user_preference(self, user: User, notification_type: str, channel: str) -> bool:
        """
        检查用户偏好设置
        """
        try:
            preference = NotificationPreference.objects.get(
                user=user,
                notification_type=notification_type,
                channel=channel
            )
            return preference.is_enabled
        except NotificationPreference.DoesNotExist:
            # 默认启用
            return True
    
    def _send_system_notification(self, notification: Notification, log: NotificationLog):
        """
        发送系统通知（数据库记录）
        """
        try:
            # 系统通知已经保存在数据库中，只需要更新状态
            log.status = 'success'
            log.sent_at = timezone.now()
            log.delivered_at = timezone.now()
            log.save()
            
        except Exception as e:
            log.status = 'failed'
            log.error_message = str(e)
            log.save()
            raise
    
    def _send_email_notification(self, notification: Notification, log: NotificationLog):
        """
        发送邮件通知
        """
        try:
            # 获取邮件渠道配置
            channel = NotificationChannel.objects.get(
                channel_type='email',
                is_active=True
            )
            
            # 发送邮件
            send_mail(
                subject=notification.title,
                message=notification.content,
                from_email=channel.configuration.get('from_email'),
                recipient_list=[notification.recipient.email],
                fail_silently=False
            )
            
            log.status = 'success'
            log.sent_at = timezone.now()
            log.delivered_at = timezone.now()
            log.save()
            
        except Exception as e:
            log.status = 'failed'
            log.error_message = str(e)
            log.save()
            raise
    
    def _send_sms_notification(self, notification: Notification, log: NotificationLog):
        """
        发送短信通知
        """
        try:
            # 获取短信渠道配置
            channel = NotificationChannel.objects.get(
                channel_type='sms',
                is_active=True
            )
            
            # 这里应该集成具体的短信服务提供商API
            # 例如阿里云短信、腾讯云短信等
            
            log.status = 'success'
            log.sent_at = timezone.now()
            log.save()
            
        except Exception as e:
            log.status = 'failed'
            log.error_message = str(e)
            log.save()
            raise
    
    def _send_push_notification(self, notification: Notification, log: NotificationLog):
        """
        发送推送通知
        """
        try:
            # 获取推送渠道配置
            channel = NotificationChannel.objects.get(
                channel_type='push',
                is_active=True
            )
            
            # 这里应该集成推送服务
            # 例如Firebase、极光推送等
            
            log.status = 'success'
            log.sent_at = timezone.now()
            log.save()
            
        except Exception as e:
            log.status = 'failed'
            log.error_message = str(e)
            log.save()
            raise
    
    def _send_webhook_notification(self, notification: Notification, log: NotificationLog):
        """
        发送Webhook通知
        """
        try:
            # 获取Webhook渠道配置
            channel = NotificationChannel.objects.get(
                channel_type='webhook',
                is_active=True
            )
            
            # 准备数据
            payload = {
                'notification_id': notification.id,
                'recipient': notification.recipient.username,
                'title': notification.title,
                'content': notification.content,
                'type': notification.notification_type,
                'priority': notification.priority,
                'timestamp': notification.created_at.isoformat()
            }
            
            # 发送请求
            response = requests.post(
                url=channel.configuration['url'],
                json=payload,
                headers=channel.configuration.get('headers', {}),
                timeout=30
            )
            
            if response.status_code == 200:
                log.status = 'success'
            else:
                log.status = 'failed'
                log.error_message = f"HTTP {response.status_code}: {response.text}"
            
            log.sent_at = timezone.now()
            log.metadata = {
                'response_status': response.status_code,
                'response_headers': dict(response.headers)
            }
            log.save()
            
        except Exception as e:
            log.status = 'failed'
            log.error_message = str(e)
            log.save()
            raise
    
    def send_test_notification(
        self,
        template: NotificationTemplate,
        recipient: str,
        context: Dict[str, Any]
    ) -> Dict[str, Any]:
        """
        发送测试通知
        """
        try:
            # 渲染模板
            rendered = self.template_service.render_template(template, context)
            
            # 这里可以发送到测试邮箱或其他测试渠道
            return {
                'status': 'success',
                'subject': rendered['subject'],
                'content': rendered['content'],
                'recipient': recipient
            }
            
        except Exception as e:
            return {
                'status': 'failed',
                'error': str(e)
            }
    
    def test_channel_connection(self, channel: NotificationChannel) -> Dict[str, Any]:
        """
        测试渠道连接
        """
        try:
            if channel.channel_type == 'email':
                return self._test_email_connection(channel)
            elif channel.channel_type == 'webhook':
                return self._test_webhook_connection(channel)
            else:
                return {
                    'status': 'success',
                    'message': f'{channel.channel_type} 渠道连接正常'
                }
                
        except Exception as e:
            return {
                'status': 'failed',
                'error': str(e)
            }
    
    def _test_email_connection(self, channel: NotificationChannel) -> Dict[str, Any]:
        """
        测试邮件连接
        """
        config = channel.configuration
        
        try:
            server = smtplib.SMTP(config['smtp_host'], config['smtp_port'])
            server.starttls()
            server.login(config['username'], config['password'])
            server.quit()
            
            return {
                'status': 'success',
                'message': '邮件服务器连接成功'
            }
            
        except Exception as e:
            return {
                'status': 'failed',
                'error': f'邮件服务器连接失败: {str(e)}'
            }
    
    def _test_webhook_connection(self, channel: NotificationChannel) -> Dict[str, Any]:
        """
        测试Webhook连接
        """
        config = channel.configuration
        
        try:
            response = requests.get(
                url=config['url'],
                headers=config.get('headers', {}),
                timeout=10
            )
            
            return {
                'status': 'success',
                'message': f'Webhook连接成功，状态码: {response.status_code}'
            }
            
        except Exception as e:
            return {
                'status': 'failed',
                'error': f'Webhook连接失败: {str(e)}'
            }


class NotificationCleanupService:
    """
    通知清理服务
    """
    
    def cleanup_old_notifications(self, days: int = 30):
        """
        清理旧通知
        """
        cutoff_date = timezone.now() - timedelta(days=days)
        
        # 删除旧的已读通知
        deleted_count = Notification.objects.filter(
            is_read=True,
            read_at__lt=cutoff_date
        ).delete()[0]
        
        logger.info(f"清理了 {deleted_count} 条旧通知")
        return deleted_count
    
    def cleanup_old_logs(self, days: int = 90):
        """
        清理旧日志
        """
        cutoff_date = timezone.now() - timedelta(days=days)
        
        deleted_count = NotificationLog.objects.filter(
            sent_at__lt=cutoff_date
        ).delete()[0]
        
        logger.info(f"清理了 {deleted_count} 条通知日志")
        return deleted_count